home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / ODF Release 3 / ODFDev / Clock / Sources / View.cpp < prev    next >
Encoding:
Text File  |  1996-12-16  |  29.7 KB  |  976 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                View.cpp
  4. //    Release Version:    $ ODF 3 $
  5. //
  6. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  7. //
  8. //========================================================================================
  9.  
  10. #ifndef VIEW_H
  11. #include "View.h"
  12. #endif
  13.  
  14. #ifndef PART_H
  15. #include "Part.h"
  16. #endif
  17.  
  18. #ifndef DEFINES_K
  19. #include "Defines.k"
  20. #endif
  21.  
  22. #ifndef CONTENT_H
  23. #include "Content.h"
  24. #endif
  25.  
  26. #ifndef FRAME_H
  27. #include "Frame.h"
  28. #endif
  29.  
  30. // ----- Part Layer -----
  31.  
  32. #ifndef FWUTIL_H
  33. #include "FWUtil.h"
  34. #endif
  35.  
  36. #ifndef FWITERS_H
  37. #include "FWIters.h"
  38. #endif
  39.  
  40. #ifndef FWCONTXT_H
  41. #include "FWContxt.h"
  42. #endif
  43.  
  44. #ifndef FWGROWBX_H
  45. #include "FWGrowBx.h"
  46. #endif
  47.  
  48. // ----- OS Layer -----
  49.  
  50. #ifndef FWTXTSHP_H
  51. #include "FWTxtShp.h"
  52. #endif
  53.  
  54. #ifndef FWRECSHP_H
  55. #include "FWRecShp.h"
  56. #endif
  57.  
  58. #ifndef FWLINSHP_H
  59. #include "FWLinShp.h"
  60. #endif
  61.  
  62. #ifndef FWOVLSHP_H
  63. #include "FWOvlShp.h"
  64. #endif
  65.  
  66. #ifndef FWODGEOM_H
  67. #include "FWODGeom.h"
  68. #endif
  69.  
  70. #ifndef FWTXTBOX_H
  71. #include "FWTxtBox.h"
  72. #endif
  73.  
  74. #ifndef FWCFMRES_H
  75. #include "FWCFMRes.h"
  76. #endif
  77.  
  78. #ifndef FWRESACC_H
  79. #include "FWResAcc.h"
  80. #endif
  81.  
  82. #ifndef FWRESTYP_H
  83. #include "FWResTyp.h"
  84. #endif
  85.  
  86. #ifndef FWFXMATH_H
  87. #include "FWFxMath.h"
  88. #endif
  89.  
  90. #ifndef FWSCRCON_H
  91. #include "FWScrCon.h"
  92. #endif
  93.  
  94. #ifndef FWBITMAP_H
  95. #include "FWBitmap.h"
  96. #endif
  97.  
  98. #ifndef FWBMPSHP_H
  99. #include "FWBmpShp.h"
  100. #endif
  101.  
  102. #ifndef FWARCSHP_H
  103. #include "FWArcShp.h"
  104. #endif
  105.  
  106. #ifndef FWGRUTIL_H
  107. #include "FWGrUtil.h"
  108. #endif
  109.  
  110. #ifndef FWRESTYP_H
  111. #include "FWResTyp.h"
  112. #endif
  113.  
  114. //========================================================================================
  115. // RunTime information
  116. //========================================================================================
  117.  
  118. #ifdef FW_BUILD_MAC
  119. #pragma segment odfclock
  120. #endif
  121.  
  122. //========================================================================================
  123. // Constants
  124. //========================================================================================
  125.  
  126. const FW_Fixed kClockRadius = FW_IntToFixed(1000);    // In logical units
  127. const FW_Fixed kFxPI = FW_DoubleToFixed(3.1415926);
  128. const FW_Fixed kUsedShapeInset = FW_DoubleToFixed(-0.003);
  129.  
  130. const FW_Boolean kAllowOffscreens = TRUE;
  131.  
  132. //========================================================================================
  133. // class CClockView
  134. //========================================================================================
  135.  
  136. FW_DEFINE_AUTO(CClockView)
  137. FW_DEFINE_CLASS_M1(CClockView, FW_CView)
  138.  
  139. //----------------------------------------------------------------------------------------
  140. // CClockView::CClockView
  141. //----------------------------------------------------------------------------------------
  142.  
  143. CClockView::CClockView(Environment* ev) :
  144.     FW_CView(ev),
  145.     fClockFrame(NULL),
  146.     fLastTime(FW_CTime::GetCurrentTime())
  147. {
  148.     SetResizeInvalidates(ev, true);
  149.  
  150.     FW_END_CONSTRUCTOR
  151. }
  152.  
  153. //----------------------------------------------------------------------------------------
  154. // CClockView::~CClockView
  155. //----------------------------------------------------------------------------------------
  156.  
  157. CClockView::~CClockView()
  158. {
  159.     FW_START_DESTRUCTOR
  160. }
  161.  
  162. //----------------------------------------------------------------------------------------
  163. // CClockView::PostCreateViewFromStream
  164. //----------------------------------------------------------------------------------------
  165.  
  166. void CClockView::PostCreateViewFromStream(Environment *ev)
  167. {
  168.     FW_CView::PostCreateViewFromStream(ev);
  169.     
  170.     fClockFrame = (CClockFrame*)GetFrame(ev);
  171.     fLastTime += fClockFrame->GetClockContent(ev)->GetTimeOffset();
  172. }
  173.  
  174. //----------------------------------------------------------------------------------------
  175. // CClockView::UpdateClock
  176. //----------------------------------------------------------------------------------------
  177.  
  178. void CClockView::UpdateClock(Environment* ev, const FW_CTime& time)
  179. {
  180.     fLastTime = time;
  181.     
  182.     FW_CFrameFacetIterator facets(ev, this->GetFrame(ev));
  183.     for (ODFacet* clockFacet = facets.First(ev); facets.IsNotComplete(ev); clockFacet = facets.Next(ev))
  184.     {
  185.         Draw(ev, clockFacet, FW_CAcquiredODShape(GetFrame(ev)->AcquireUsedShape(ev, NULL)));
  186.     }
  187. }
  188.  
  189. //========================================================================================
  190. // class CAnalogView
  191. //========================================================================================
  192.  
  193. FW_DEFINE_AUTO(CAnalogView)
  194. FW_DEFINE_CLASS_M1(CAnalogView, CClockView)
  195.     
  196. const FW_ClassTypeConstant LAnalogView = FW_TYPE_CONSTANT('A','N','V','W');
  197. FW_REGISTER_ARCHIVABLE_CLASS(LAnalogView, CAnalogView, CAnalogView::Create, FW_CView::Read, CAnalogView::Destroy, FW_CView::Write)
  198.  
  199. //----------------------------------------------------------------------------------------
  200. // CAnalogView::CAnalogView
  201. //----------------------------------------------------------------------------------------
  202.  
  203. CAnalogView::CAnalogView(Environment* ev) :
  204.     CClockView(ev)
  205. {
  206.     FW_END_CONSTRUCTOR
  207. }
  208.  
  209. //----------------------------------------------------------------------------------------
  210. // CAnalogView::~CAnalogView
  211. //----------------------------------------------------------------------------------------
  212.  
  213. CAnalogView::~CAnalogView()
  214. {
  215.     FW_START_DESTRUCTOR
  216. }
  217.  
  218. //----------------------------------------------------------------------------------------
  219. //    FixedCosine
  220. //----------------------------------------------------------------------------------------
  221.  
  222. static FW_Fixed FixedCosine(FW_Fixed angleRadians)
  223. {
  224. #if (GENERATING68K)
  225.     FW_Fixed hackResult;
  226.     hackResult.fRep = ::Frac2Fix(FracCos(angleRadians.fRep));
  227.     return hackResult;
  228. #else
  229.     return FW_Cos(angleRadians);
  230. #endif
  231. }
  232.  
  233. //----------------------------------------------------------------------------------------
  234. //    FixedSine
  235. //----------------------------------------------------------------------------------------
  236.  
  237. static FW_Fixed FixedSine(FW_Fixed angleRadians)
  238. {
  239. #if (GENERATING68K)
  240.     FW_Fixed hackResult;
  241.     hackResult.fRep = ::Frac2Fix(FracSin(angleRadians.fRep));
  242.     return hackResult;
  243. #else
  244.     return FW_Sin(angleRadians);
  245. #endif
  246. }
  247.  
  248. //----------------------------------------------------------------------------------------
  249. // CAnalogView::GetTickColor
  250. //----------------------------------------------------------------------------------------
  251.  
  252. static void GetTickColor(const FW_CColor& backgroundColor, FW_CColor& color)
  253. {
  254.     color = backgroundColor;
  255.     
  256.     if(color.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  257.         color += 0x40; // make it brighter
  258.     else
  259.         color -= 0x40; // make it darker
  260. }
  261.  
  262. //----------------------------------------------------------------------------------------
  263. // CAnalogView::GetHoursColor - Get the color of the hours hand
  264. //----------------------------------------------------------------------------------------
  265.  
  266. static void GetHoursColor(const FW_CColor& backgroundColor, FW_CColor& color)
  267. {
  268.     color = backgroundColor;
  269.     
  270.     if(color.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  271.         color += 0x40; // make it brighter
  272.     else
  273.         color -= 0x40; // make it darker
  274. }
  275.  
  276. //----------------------------------------------------------------------------------------
  277. // CAnalogView::GetMinutesColor - Get the color of the minutes hand
  278. //----------------------------------------------------------------------------------------
  279.  
  280. static void GetMinutesColor(const FW_CColor& backgroundColor, FW_CColor& color)
  281. {
  282.     GetHoursColor(backgroundColor, color);
  283.     
  284.     if(backgroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  285.         color += 0x40; // make it brighter
  286.     else
  287.         color -= 0x40; // make it darker
  288. }
  289.  
  290. //----------------------------------------------------------------------------------------
  291. // CAnalogView::GetSecondsColor - Get the color of the seconds hand
  292. //----------------------------------------------------------------------------------------
  293.  
  294. static void GetSecondsColor(const FW_CColor& backgroundColor, FW_CColor& color)
  295. {
  296.     if(backgroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  297.         color = FW_kRGBWhite;
  298.     else
  299.         color = FW_kRGBBlack;
  300. }
  301.  
  302. //----------------------------------------------------------------------------------------
  303. // CAnalogView::DoUpdateClockFacet
  304. //----------------------------------------------------------------------------------------
  305.  
  306. void CAnalogView::DoUpdateClockFacet(Environment* ev, 
  307.                                     FW_CGraphicContext* gc, 
  308.                                     const FW_CRect& clockRect, 
  309.                                     const FW_CTime& newTime,
  310.                                     Boolean eraseAsCircle)
  311. {
  312. FW_UNUSED(ev);
  313. FW_UNUSED(clockRect); // this is not in clock coordinates
  314.  
  315.         FW_CRect clockBox(-kClockRadius, -kClockRadius, kClockRadius, kClockRadius);
  316.  
  317.         FW_CColor backGroundColor;
  318.         fClockFrame->GetBackgroundColor(&backGroundColor);
  319.  
  320.         // erase the background
  321.         
  322.         if(eraseAsCircle)
  323.             FW_COvalShape::RenderOval(*gc, clockBox, FW_kFill, backGroundColor);
  324.         else
  325.             FW_CRectShape::RenderRect(*gc, clockBox, FW_kFill, backGroundColor);
  326.  
  327.         // and draw it
  328.         this->DrawClockFace(ev, gc);
  329.         
  330.         this->DrawHourHand(ev, gc, newTime.GetHour(), newTime.GetMinute());
  331.         this->DrawMinuteHand(ev, gc, newTime.GetMinute(), newTime.GetSecond());
  332.         this->DrawSecondHand(ev, gc, newTime.GetSecond());
  333. }
  334.  
  335. //----------------------------------------------------------------------------------------
  336. // CAnalogView::Draw
  337. //----------------------------------------------------------------------------------------
  338.  
  339. void CAnalogView::Draw(Environment *ev, ODFacet* odFacet, ODShape* invalidShape)
  340. {
  341.     ODCanvas* canvas = odFacet->GetCanvas(ev);
  342.     FW_Boolean useOffscreenDrawing = kAllowOffscreens && canvas->IsDynamic(ev) && !canvas->IsOffscreen(ev);
  343.     
  344.     FW_CAcquiredODShape aqUsedShape = GetFrame(ev)->AcquireUsedShape(ev, NULL);
  345.     FW_CAcquiredODShape aqFrameShape = GetFrame(ev)->AcquireFrameShape(ev, NULL);
  346.     FW_CRect usedBox = FW_GetShapeBoundingBox(ev, aqUsedShape);
  347.     FW_CRect frameBox = FW_GetShapeBoundingBox(ev, aqFrameShape);
  348.  
  349.     // create a mapping object
  350.     FW_CMapping mapping(FW_kCustomConstrained);
  351.     
  352.     if( ! useOffscreenDrawing )
  353.     {
  354.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  355.  
  356.         FW_SPoint res;
  357.         FW_PrivGDev_GetResolution(vc.GetGraphicDevice(), res);
  358.         
  359.         // the device extents must be in units of device resolution.
  360.         // since the frame is in content resolution, it must  be scaled.
  361.         FW_CPoint deviceExtent;
  362.         deviceExtent.x = frameBox.Width() * (res.x / FW_kFixed72);
  363.         deviceExtent.y = frameBox.Height() * (res.y / FW_kFixed72);
  364.  
  365.         // logical space based on kClockRadius and ratio of frameBox to usedBox
  366.         FW_CPoint logicalCenter;
  367.         logicalCenter.x = (frameBox.Width() / usedBox.Width()) * kClockRadius;
  368.         logicalCenter.y = (frameBox.Height() / usedBox.Height()) * kClockRadius;
  369.  
  370.         FW_CPoint logicalExtent(FW_MultipliedByInt(logicalCenter.x, 2), FW_MultipliedByInt(logicalCenter.y, 2));
  371.                 
  372.         mapping.SetExtents(ev, logicalExtent, deviceExtent);
  373.         
  374.         // we want {0, 0} to be in the middle of of the device
  375.         mapping.SetLogicalOrigin(ev, -logicalCenter.x, -logicalCenter.y);        
  376.  
  377.         vc.SetMapping(mapping);
  378.         
  379.         DoUpdateClockFacet(ev, &vc, frameBox, fLastTime, true);
  380.     }
  381.     else
  382.     {
  383.         // The offscreen business for the analog view is different.
  384.         // the logical space (in which all drawing is performed) is
  385.         // fixed (-kClockRadius, -kClockRadius, kClockRadius, kClockRadius)
  386.         // while the physical (or device) space is the actual offscreen size,
  387.         // which is based on the used shape.
  388.  
  389.         // we use a logical coordinate space based on kClockRadius...
  390.         FW_CPoint logicalExtent(FW_MultipliedByInt(kClockRadius, 2), FW_MultipliedByInt(kClockRadius, 2));
  391.         
  392.         // within the device space defined by usedBox
  393.         FW_CPoint deviceExtent(usedBox.Width(), usedBox.Height());
  394.  
  395.         mapping.SetExtents(ev, logicalExtent, deviceExtent);
  396.         
  397.         // create an offscreen bitmap
  398.         FW_CBitmap bits(FW_FixedToInt(deviceExtent.x), FW_FixedToInt(deviceExtent.y), 0, NULL);
  399.         
  400.         {
  401.             // create a graphics context so that we can draw _to_ the bitmap
  402.             FW_CBitmapContext vc(ev, bits);
  403.  
  404.             // we want -kClockRadius, -kClockRadius to be at the top left of the bitmap system
  405.             mapping.SetLogicalOrigin(ev, -kClockRadius, -kClockRadius);
  406.  
  407.             // install the mapping
  408.             vc.SetMapping(mapping);
  409.             
  410.             DoUpdateClockFacet(ev, &vc, usedBox, fLastTime, false);
  411.             
  412.             // ----- Set the Highlight -----
  413.             if (odFacet->GetHighlight(ev) == kODFullHighlight)
  414.             {
  415.                 FW_CRect clockRect(-kClockRadius, -kClockRadius, kClockRadius, kClockRadius);
  416.                 FW_CRectShape::RenderRect(vc, clockRect, FW_kFill, FW_CInk(FW_kSystemHilite));
  417.             }
  418.             // the bitmap context is destructed here, but the bits remain
  419.         }
  420.  
  421.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  422.             
  423.         // render the bitmap
  424.         FW_CBitmapShape::RenderBitmap(vc, bits, usedBox);
  425.     }
  426. }
  427.  
  428. //----------------------------------------------------------------------------------------
  429. // CAnalogView::DrawTicks
  430. //----------------------------------------------------------------------------------------
  431.  
  432. static void DrawTicks(FW_CGraphicContext* gc, FW_CBoundedShape& tickShape, 
  433.                      FW_Fixed xPos, FW_Fixed yPos, 
  434.                      FW_Fixed tickWidth)
  435. {
  436.     tickWidth = FW_DividedByInt(tickWidth, 2);
  437.     
  438.     tickShape.SetRectangle(FW_CRect(xPos - tickWidth, yPos - tickWidth, xPos + tickWidth, yPos + tickWidth));
  439.     tickShape.Render(*gc);
  440.  
  441.     tickShape.SetRectangle(FW_CRect( - xPos - tickWidth, yPos - tickWidth, - xPos + tickWidth, yPos + tickWidth));
  442.     tickShape.Render(*gc);
  443.  
  444.     tickShape.SetRectangle(FW_CRect(xPos - tickWidth, - yPos - tickWidth, xPos + tickWidth, - yPos + tickWidth));
  445.     tickShape.Render(*gc);
  446.  
  447.     tickShape.SetRectangle(FW_CRect( - xPos - tickWidth, - yPos - tickWidth, - xPos + tickWidth, - yPos + tickWidth));
  448.     tickShape.Render(*gc);
  449. }
  450.  
  451. //----------------------------------------------------------------------------------------
  452. // CAnalogView::DrawClockFace
  453. //----------------------------------------------------------------------------------------
  454.  
  455. void CAnalogView::DrawClockFace(Environment* ev, FW_CGraphicContext* gc)
  456. {
  457. FW_UNUSED(ev);
  458.     // ----- Erase under the clock -----
  459.     FW_CRect ovalRect(-kClockRadius, -kClockRadius, kClockRadius, kClockRadius);
  460.     
  461.     FW_CColor backGroundColor;
  462.     fClockFrame->GetBackgroundColor(&backGroundColor);
  463.  
  464.     FW_CColor tickColor;
  465.     GetTickColor(backGroundColor, tickColor);
  466.  
  467.     FW_CColor rimColor;
  468.     if(backGroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  469.         rimColor = FW_kRGBWhite;
  470.     else
  471.         rimColor = FW_kRGBBlack;
  472.         
  473.     FW_COvalShape::RenderOval(
  474.         *gc,
  475.         ovalRect,
  476.         FW_kFrame,
  477.         rimColor,
  478.         FW_CStyle(kClockRadius * FW_DoubleToFixed(0.02)));
  479.  
  480.     // ----- Render the "ODF" string -----
  481.     
  482.     FW_CString faceString = fClockFrame->GetClockContent(ev)->GetFaceString();
  483.     
  484.     FW_CTextShape::RenderText(
  485.         *gc,
  486.         faceString,
  487.         FW_CPoint(FW_IntToFixed(0), - FW_Half(kClockRadius)),
  488.         FW_CFont(FW_GetHelveticaFontName(), FW_kItalic, FW_DividedByInt(kClockRadius, 5)),
  489.         FW_kTextAlignHCenter | FW_kTextAlignBaseLine, FW_CInk(tickColor, FW_kRGBWhite, FW_kOr));
  490.     
  491.     
  492.     // ----- Render the ticks around the clock face -----
  493.  
  494.     // create a rectangle shape for the small ticks
  495.     FW_CRectShape rectShape;
  496.     rectShape.SetInk(FW_CInk(tickColor));
  497.  
  498.     // create an oval shape for the large ticks
  499.     FW_COvalShape ovalShape(ovalRect, FW_kFill);
  500.     ovalShape.SetInk(FW_CInk(tickColor));
  501.  
  502.     // Use the symmetry of the clock face to speed up the calculations for drawing the face
  503.     // We only need to calculate points for 45 degrees of the circle.  The remaining points can
  504.     // be inferred from these points
  505.     
  506.     short angle = 0;
  507.     short fiveMinute = 0;
  508.  
  509.     while (angle < 45)
  510.     {
  511.         // ----- convert angle to radians -----
  512.         FW_Fixed radians = FW_DividedByInt(kFxPI * FW_IntToFixed(90 - angle), 180);
  513.         FW_Fixed cosRadian = FixedCosine(radians);
  514.         FW_Fixed sinRadian = FixedSine(radians);
  515.         
  516.         FW_Fixed tickRadius = kClockRadius * FW_DoubleToFixed(0.9);
  517.         FW_Fixed xTick = tickRadius * cosRadian;
  518.         FW_Fixed yTick = tickRadius * sinRadian;
  519.  
  520.         if(fiveMinute == 0)
  521.         {
  522.             fiveMinute = 4;
  523.             
  524.             FW_Fixed tickWidth = FW_DividedByInt(kClockRadius, 15);
  525.             
  526.             DrawTicks(gc, ovalShape, xTick, yTick, tickWidth);
  527.             
  528.             DrawTicks(gc, ovalShape, yTick, xTick, tickWidth);
  529.         }
  530.         else
  531.         {
  532.             fiveMinute--;
  533.             
  534.             FW_Fixed tickWidth = FW_DividedByInt(kClockRadius, 30);
  535.             
  536.             DrawTicks(gc, rectShape, xTick, yTick, tickWidth);
  537.             
  538.             DrawTicks(gc, rectShape, yTick, xTick, tickWidth);
  539.         }
  540.         
  541.         angle += 6; // a tick occurs every six degrees
  542.     }
  543. }
  544.  
  545. //----------------------------------------------------------------------------------------
  546. //    CalcPoint
  547. //----------------------------------------------------------------------------------------
  548.  
  549. static FW_CPoint CalcPoint(FW_Fixed radius, FW_Fixed angle)
  550. {
  551.     return FW_CPoint(
  552.         radius * FixedSine(angle),
  553.         - radius * FixedCosine(angle));
  554. }
  555.  
  556. //----------------------------------------------------------------------------------------
  557. // ::DegreesToRadians
  558. //----------------------------------------------------------------------------------------
  559.  
  560. static inline FW_Fixed DegreesToRadians(FW_Fixed degrees)
  561. {
  562.     return FW_DividedByInt(degrees * kFxPI, 180);
  563. }
  564.  
  565. //----------------------------------------------------------------------------------------
  566. // CAnalogView::DrawHourHand
  567. //----------------------------------------------------------------------------------------
  568.  
  569. void CAnalogView::DrawHourHand(Environment* ev, 
  570.                                 FW_CGraphicContext* gc, 
  571.                                 short hour, 
  572.                                 short minute)
  573. {
  574. FW_UNUSED(ev);
  575.     FW_Fixed hourRadius = kClockRadius * FW_DoubleToFixed(0.5);    // Make the hour hand short
  576.  
  577.     if (hour > 11)
  578.         hour -= 12;
  579.     
  580.     // ----- Divide the clock face into degrees.  
  581.     // ----- Hour hand falls on every five minutes or every 30 degrees
  582.     
  583.     // ----- since arcs may only be drawn for whole degrees, we use integer math
  584.     const short kDegreesPerHour = 30;
  585.     short clockDegrees = (hour * kDegreesPerHour) + ((minute * kDegreesPerHour) / 60);
  586.     FW_Fixed radians = ::DegreesToRadians(FW_IntToFixed(clockDegrees));
  587.     
  588.     // calculate the point where the tip of the hand should end
  589.     FW_CPoint newPoint = ::CalcPoint(hourRadius, radians);
  590.  
  591.     const short arcDegrees = 14; // the hand is a filled arc this many degrees wide
  592.     // adjust angle so the base of the arc is centered
  593.     short angle = clockDegrees - (arcDegrees / 2);
  594.     
  595.     // flip it to the other side
  596.     angle += 180;
  597.     
  598.     FW_CRect faceRect(-hourRadius, -hourRadius, hourRadius, hourRadius);
  599.  
  600.     // offset the reference rectangle so the center is where we want the vertex of the arc
  601.     faceRect.Offset(newPoint.x, newPoint.y);
  602.     
  603.  
  604.     FW_CColor backGroundColor;
  605.     fClockFrame->GetBackgroundColor(&backGroundColor);
  606.  
  607.     FW_CColor color;
  608.     GetHoursColor(backGroundColor, color);
  609.     
  610.     FW_CArcShape::RenderArc(*gc, faceRect,
  611.                             angle,
  612.                             arcDegrees,
  613.                             FW_kFill,
  614.                             color);
  615.  
  616. }
  617.  
  618. //----------------------------------------------------------------------------------------
  619. // CAnalogView::DrawMinuteHand
  620. //----------------------------------------------------------------------------------------
  621.  
  622. void CAnalogView::DrawMinuteHand(Environment* ev, 
  623.                                 FW_CGraphicContext* gc, 
  624.                                 short minute,
  625.                                 short second)
  626. {
  627. FW_UNUSED(ev);
  628.     FW_Fixed minuteRadius = kClockRadius * FW_DoubleToFixed(0.8);  // Make the minute hand longer than the hour hand
  629.     
  630.     // ----- Divide the clock face into degrees.  
  631.     // ----- (360 degrees divided by 60 minutes is 6 degrees per minute)
  632.     const short kDegreesPerMinute = 6;
  633.     short clockDegrees = (minute * kDegreesPerMinute) + (second * kDegreesPerMinute) / 60;
  634.     FW_Fixed radians = ::DegreesToRadians(FW_IntToFixed(clockDegrees));
  635.  
  636.     FW_CPoint newPoint = ::CalcPoint(minuteRadius, radians);
  637.  
  638.     const short arcDegrees = 6; // the hand is a filled arc this many degrees wide
  639.     // adjust angle so the base of the arc is centered
  640.     short angle = clockDegrees - (arcDegrees / 2);
  641.  
  642.     // flip it to the other side
  643.     angle += 180;
  644.     
  645.     FW_CRect faceRect(-minuteRadius, -minuteRadius, minuteRadius, minuteRadius);
  646.  
  647.     // offset the reference rectangle so the center is where we want the vertex of the arc
  648.     faceRect.Offset(newPoint.x, newPoint.y);
  649.  
  650.     FW_CColor backGroundColor;
  651.     fClockFrame->GetBackgroundColor(&backGroundColor);
  652.  
  653.     FW_CColor color;
  654.     GetMinutesColor(backGroundColor, color);
  655.  
  656.     FW_CArcShape::RenderArc(*gc, faceRect,
  657.                             angle,
  658.                             arcDegrees,
  659.                             FW_kFill,
  660.                             color);
  661.  
  662. }
  663.  
  664. //----------------------------------------------------------------------------------------
  665. // CAnalogView::DrawSecondHand
  666. //----------------------------------------------------------------------------------------
  667.  
  668. void CAnalogView::DrawSecondHand(Environment* ev, 
  669.                                 FW_CGraphicContext* gc, 
  670.                                 short second)
  671. {
  672. FW_UNUSED(ev);
  673.     FW_Fixed secondRadius = kClockRadius * FW_DoubleToFixed(0.9);    // A nice sweeping second hand
  674.  
  675.     // ----- Divide the clock face into degrees.  
  676.     // ----- (360 degrees divided by 60 seconds is 6 degrees per second)
  677.     
  678.     FW_Fixed radians = ::DegreesToRadians(FW_IntToFixed(second * 6));
  679.     
  680.     FW_CPoint newPoint = ::CalcPoint(secondRadius, radians);
  681.  
  682.     FW_CLineShape lineShape(FW_kZeroPoint, newPoint);
  683.     FW_CColor backGroundColor;
  684.     fClockFrame->GetBackgroundColor(&backGroundColor);
  685.  
  686.     FW_CColor color;
  687.     GetSecondsColor(backGroundColor, color);
  688.  
  689.     lineShape.SetInk(FW_CInk(color));
  690.     lineShape.Render(*gc);
  691.     
  692.     // draw a "pin" in the center
  693.     FW_Fixed pinRadius = FW_DividedByInt(kClockRadius, 40);
  694.     FW_CRect pinRect(-pinRadius, -pinRadius, pinRadius, pinRadius);
  695.     FW_COvalShape::RenderOval(*gc, pinRect, FW_kFill, color);
  696. }
  697.  
  698. //----------------------------------------------------------------------------------------
  699. //    CAnalogView::CreateUsedShape
  700. //----------------------------------------------------------------------------------------
  701.  
  702. ODShape* CAnalogView::CreateUsedShape(Environment* ev, const FW_CRect& suggestedUsedRect)
  703. {    
  704.     FW_CRect rect(suggestedUsedRect);
  705.     FW_Fixed height = suggestedUsedRect.Height();
  706.     FW_Fixed width = suggestedUsedRect.Width();
  707.     
  708.     if (height < width)
  709.         rect.right = rect.left + height;
  710.     else
  711.         rect.bottom = rect.top + width;
  712.  
  713.     rect.PlaceInCenterOf(suggestedUsedRect);
  714.     
  715.     return ::FW_CreateOvalODShape(ev, rect);
  716. }
  717.  
  718. //----------------------------------------------------------------------------------------
  719. //    CAnalogView::Create
  720. //----------------------------------------------------------------------------------------
  721.  
  722. void* CAnalogView::Create(FW_CReadableStream& stream, FW_ClassTypeConstant type)
  723. {
  724. FW_UNUSED(stream);
  725. FW_UNUSED(type);
  726.     FW_SOMEnvironment ev;
  727.     return FW_NEW(CAnalogView, (ev));
  728. }
  729.  
  730. //----------------------------------------------------------------------------------------
  731. //    CDrawView::Destroy
  732. //----------------------------------------------------------------------------------------
  733.  
  734. void CAnalogView::Destroy(void* object, FW_ClassTypeConstant type)
  735. {
  736. FW_UNUSED(type);
  737.     CAnalogView* self = (CAnalogView*) object;
  738.     delete self;
  739. }
  740.  
  741. //========================================================================================
  742. // class CDigitalView
  743. //========================================================================================
  744.  
  745. FW_DEFINE_AUTO(CDigitalView)
  746. FW_DEFINE_CLASS_M1(CDigitalView, CClockView)
  747.     
  748. const FW_ClassTypeConstant LDigitalView = FW_TYPE_CONSTANT('D','G','V','W');
  749. FW_REGISTER_ARCHIVABLE_CLASS(LDigitalView, CDigitalView, CDigitalView::Create, FW_CView::Read, CDigitalView::Destroy, FW_CView::Write)
  750.  
  751.     
  752. //----------------------------------------------------------------------------------------
  753. // CDigitalView::CDigitalView
  754. //----------------------------------------------------------------------------------------
  755.  
  756. CDigitalView::CDigitalView(Environment* ev) :
  757.     CClockView(ev),
  758.     fFont(FW_kCourier12)
  759. {
  760.     FW_END_CONSTRUCTOR
  761. }
  762.  
  763. //----------------------------------------------------------------------------------------
  764. // CDigitalView::~CDigitalView
  765. //----------------------------------------------------------------------------------------
  766.  
  767. CDigitalView::~CDigitalView()
  768. {
  769.     FW_START_DESTRUCTOR
  770. }
  771.  
  772. //----------------------------------------------------------------------------------------
  773. //    CDigitalView::GetDigitalClockRect
  774. //----------------------------------------------------------------------------------------
  775.  
  776. FW_CRect CDigitalView::GetDigitalClockRect(Environment* ev, const FW_CRect& suggestedUsedRect)
  777. {
  778.     // ----- Calculate a default rectangle size for a digital clock
  779.     FW_CString32 digitalString;
  780.     {
  781.         FW_PSharedLibraryResourceFile resFile(ev);
  782.         ::FW_LoadStringByID(ev, resFile, kClockFaceStrings, FW_kMultiStringRes, kClockDigitalWidthString, digitalString);
  783.     }
  784.     
  785.     FW_CTextShape textShape(digitalString,
  786.                             FW_IntToFixed(0),
  787.                             FW_IntToFixed(0),
  788.                             fFont);
  789.  
  790.     FW_CScreenContext sc(ev); // we need a context for measuring the text
  791.  
  792.     short boxHeight = FW_FixedToInt(suggestedUsedRect.Height());
  793.     short boxWidth = FW_FixedToInt(suggestedUsedRect.Width());
  794.     short fontSize = boxHeight;
  795.     FW_CRect boundsRect;
  796.     
  797.     for(;;)
  798.     {
  799.         fFont.SetFontSize(FW_IntToFixed(fontSize));
  800.         FW_CFontMetrics metrics;
  801.         fFont.GetFontMetrics(sc, metrics);
  802.         short fontHeight = metrics.GetFontHeight();
  803.         if(fontHeight > boxHeight)
  804.         {
  805.             fontSize = ( (long) fontSize * boxHeight) / fontHeight;
  806.         }
  807.         else
  808.         {
  809.             textShape.GetBounds(sc, boundsRect);
  810.             short textWidth = FW_FixedToInt(boundsRect.Width());
  811.             if(boxWidth < textWidth)
  812.                 fontSize = ( (long) fontSize * boxWidth) / textWidth;
  813.             else
  814.             {
  815.                 break;
  816.             }
  817.         }
  818.         
  819.         if(fontSize == 0) // punt
  820.         {
  821.             fFont.SetFontSize(FW_IntToFixed(12));
  822.             textShape.GetBounds(sc, boundsRect);
  823.             break;
  824.         }
  825.     }
  826.     
  827.     boundsRect.PlaceInCenterOf(suggestedUsedRect);
  828.  
  829.     return boundsRect;
  830. }
  831.         
  832. //----------------------------------------------------------------------------------------
  833. // CDigitalView::DoUpdateClockFacet
  834. //----------------------------------------------------------------------------------------
  835.  
  836. void CDigitalView::DoUpdateClockFacet(Environment* ev, 
  837.                                     FW_CGraphicContext* gc, 
  838.                                     const FW_CRect& clockRect, 
  839.                                     const FW_CTime& newTime)
  840. {
  841. FW_UNUSED(ev);
  842.     FW_CString255 timeString;
  843.     newTime.GetTimeString(timeString, TRUE);
  844.         
  845.     FW_CColor backGroundColor;
  846.     fClockFrame->GetBackgroundColor(&backGroundColor);
  847.  
  848.     FW_CColor contrastColor;
  849.     if(backGroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  850.         contrastColor = FW_kRGBWhite;
  851.     else
  852.         contrastColor = FW_kRGBBlack;
  853.  
  854.     FW_CRectShape::RenderRect(*gc, clockRect, FW_kFill, backGroundColor);
  855.     FW_CRectShape::RenderRect(*gc, clockRect, FW_kFrame, contrastColor);
  856.     
  857.     FW_CTextShape::RenderText(*gc, timeString, clockRect.Center(), fFont,
  858.         FW_kTextAlignHCenter | FW_kTextAlignVCenter,
  859.         FW_CInk(contrastColor, FW_kRGBWhite, FW_kOr));
  860. }
  861.  
  862. //----------------------------------------------------------------------------------------
  863. // CDigitalView::Draw
  864. //----------------------------------------------------------------------------------------
  865.  
  866. void CDigitalView::Draw(Environment* ev, ODFacet* odFacet, ODShape* invalidShape)
  867. {    
  868.     ODCanvas* canvas = odFacet->GetCanvas(ev);
  869.     FW_Boolean useOffscreenDrawing = kAllowOffscreens && canvas->IsDynamic(ev) && !canvas->IsOffscreen(ev);
  870.  
  871.     FW_CAcquiredODShape aqUsedShape(GetFrame(ev)->AcquireUsedShape(ev, NULL));
  872.     FW_CRect clockBox = FW_GetShapeBoundingBox(ev, aqUsedShape);
  873.     clockBox.Inset(kClockRadius * -kUsedShapeInset, kClockRadius * -kUsedShapeInset);
  874.  
  875.     if( ! useOffscreenDrawing )
  876.     {
  877.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  878.  
  879.         DoUpdateClockFacet(ev, &vc, clockBox, fLastTime);
  880.     }
  881.     else
  882.     {
  883.         // create an offscreen bitmap the size of our used shape
  884.         FW_CBitmap bits(FW_FixedToInt(clockBox.Width()), FW_FixedToInt(clockBox.Height()), 0, NULL);
  885.         
  886.         {
  887.             // create a graphics context so that we can draw TO the bitmap
  888.             FW_CBitmapContext vc(ev, bits);
  889.  
  890.             // because the mapping of the bitmap has its origin at 0, and the
  891.             // used shape bounds upon which DoUpdateClockFacet bases its mapping
  892.             // is probably not at 0, 0, we change the logical mapping so that the clock
  893.             // draws in the right spot.
  894.             FW_CMapping mapping;
  895.             vc.GetMapping(mapping);
  896.             mapping.SetLogicalOrigin(ev, clockBox.left, clockBox.top);
  897.             vc.SetMapping(mapping);
  898.  
  899.             // we don't need to prepare the offscreen since DoUpdateClockFacet
  900.             // will paint the whole image. Otherwise...
  901.             // FW_CRectShape::RenderRect(vc, box, FW_kFill, FW_kRGBWhite);
  902.             
  903.             DoUpdateClockFacet(ev, &vc, clockBox, fLastTime);
  904.             
  905.             // ----- Set the Highlight -----
  906.             if (odFacet->GetHighlight(ev) == kODFullHighlight)
  907.                 FW_CRectShape::RenderRect(vc, clockBox, FW_kFill, FW_CInk(FW_kSystemHilite));
  908.             
  909.             // the bitmap context is destructed here, but the bits remain
  910.         }
  911.  
  912.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  913.         
  914.         // render the bitmap
  915.         FW_CBitmapShape::RenderBitmap(vc, bits, clockBox);
  916.     }
  917. }
  918.  
  919. //----------------------------------------------------------------------------------------
  920. //    CDigitalView::CreateUsedShape
  921. //----------------------------------------------------------------------------------------
  922.  
  923. ODShape* CDigitalView::CreateUsedShape(Environment* ev, const FW_CRect& suggestedUsedRect)
  924. {    
  925.     FW_CRect rect = GetDigitalClockRect(ev, suggestedUsedRect);
  926.  
  927.     rect.Inset(kClockRadius * kUsedShapeInset, kClockRadius * kUsedShapeInset);
  928.  
  929.     // Below shows two equivalent methods for specifying the used shape, the first
  930.     // as a region, and the second as an ODPolygon with one contour.
  931.     
  932.     // The first method is not likely to fail, so the second method will probably never
  933.     // get used. It's here as an example. [BRP]
  934.  
  935.     const short kCorners = 4;
  936.     FW_CPoint rectCorners[kCorners] = {
  937.         FW_CPoint(rect.left, rect.top), 
  938.         FW_CPoint(rect.right, rect.top), 
  939.         FW_CPoint(rect.right, rect.bottom), 
  940.         FW_CPoint(rect.left, rect.bottom)
  941.     };
  942.     
  943.     ODShape *shape = ::FW_CreatePolygonODShape(ev, kCorners, rectCorners);
  944.  
  945.     if(shape == NULL)
  946.         shape = ::FW_NewODShape(ev, rect);
  947.         
  948.     return shape;
  949. }
  950.  
  951. //----------------------------------------------------------------------------------------
  952. //    CDigitalView::Create
  953. //----------------------------------------------------------------------------------------
  954.  
  955. void* CDigitalView::Create(FW_CReadableStream& stream, FW_ClassTypeConstant type)
  956. {
  957. FW_UNUSED(stream);
  958. FW_UNUSED(type);
  959.  
  960.     FW_SOMEnvironment ev;
  961.     return FW_NEW(CDigitalView, (ev));
  962. }
  963.  
  964. //----------------------------------------------------------------------------------------
  965. //    CDigitalView::Destroy
  966. //----------------------------------------------------------------------------------------
  967.  
  968. void CDigitalView::Destroy(void* object, FW_ClassTypeConstant type)
  969. {
  970. FW_UNUSED(type);
  971.  
  972.     CDigitalView* self = (CDigitalView*) object;
  973.     delete self;
  974. }
  975.  
  976.